/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package drt;

import evaluation.Graph;
import evaluation.Vertex;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import logic.ComplexType;
import logic.Type;

/**
 *
 * @author phong
 */
public class DrtApplicationExpression extends DrtStandAloneExpression {
    
    DrtExpression func;
    DrtExpression arg;
    
    public DrtApplicationExpression(DrtExpression func, DrtExpression arg) {
        this.func = func;
        this.arg = arg;
        
        Type funcType = this.func.getType();
        Type argType = this.arg.getType();
        
        if (!funcType.equals(Type.UNKNOWN_TYPE) && !argType.equals(Type.UNKNOWN_TYPE)) {
            if (!(funcType instanceof ComplexType) || !((ComplexType)funcType).getFirst().matches(argType))
                throw new Error("incorrect types");
        }
    }
    
    public static DrtApplicationExpression make(DrtExpression func, List<DrtExpression> args) {
        DrtExpression f = func;
        for (DrtExpression arg : args) {
            f = new DrtApplicationExpression(f, arg);
        }
        return (DrtApplicationExpression)f;
    }
    
    public DrtExpression getFunc() {
        return this.func;
    }
    
    public DrtExpression getArg() {
        return this.arg;
    }
    
    @Override
    public Type getType() {
        Type funcType = this.func.getType();
        if (funcType.equals(Type.UNKNOWN_TYPE))
            return Type.UNKNOWN_TYPE;
        else { 
            return ((ComplexType)funcType).getSecond();
        }
    }
    
    /*
     * don't use this function
     */
    @Override
    public void setType(Type t) {
    }
    
    @Override
    public void getRefs(LinkedHashSet<DRSReferent> refs) {
        this.func.getRefs(refs);
        this.arg.getRefs(refs);
    }
    
    @Override
    public void getAllVariables(LinkedHashSet<DrtVariable> vars) {
        this.func.getAllVariables(vars);
        this.arg.getAllVariables(vars);
    }
    
    @Override
    public DrtExpression simplify() {
        DrtExpression fu = this.func.simplify();
        DrtExpression ar = this.arg.simplify();
        
        if (fu instanceof DrtLambdaExpression) {
            DrtExpression f = ((DrtLambdaExpression)fu).getTerm();
            f = f.replace(((DrtLambdaExpression)fu).getVar(), ar).simplify();
            return f;
        }
        else 
            return new DrtApplicationExpression(fu, ar);
    }
    
    @Override
    public DrtApplicationExpression replace(DrtVariable var, DrtExpression exp) {
        return new DrtApplicationExpression(
                func.replace(var, exp), 
                arg.replace(var, exp));
    }
    
    @Override
    public String toString() {
        return "(" + this.func.toString() + "@" + this.arg.toString() + ")";
    }
    
    @Override
    public boolean equals(Object other) {
        if (other instanceof DrtApplicationExpression) {
            DrtApplicationExpression that = (DrtApplicationExpression)other;

            // alpha-convert
            LinkedHashSet<DrtVariable> thisVars = new LinkedHashSet<DrtVariable>();
            this.getAllVariables(thisVars);
            
            DrtApplicationExpression repThat = (DrtApplicationExpression)that.alphaConvert(thisVars);
            if (repThat == null) return false;

            return this.arg.equals(repThat.arg) && this.func.equals(repThat.func);
        }
        return false;
    }
    
    @Override
    public int hashCode() {
        int hc = "applicationexpression".hashCode();
        if (!(this.arg instanceof DrtVariable))
            hc += this.arg.hashCode() * 17;
        if (!(this.func instanceof DrtVariable))
            hc += this.func.hashCode() * 31;
        return hc;
    }

    @Override
    public Graph toGraph() {
        Graph g = new Graph();
        
        Graph g1 = this.func.toGraph();
        Graph g2 = this.arg.toGraph();
        
        for (Vertex v : g1.getMarks()) {
            g.addMarks(v);
        }
        for (Vertex v : g2.getMarks()) {
            g.addMarks(v);
        }
        
        g.merge(g1);
        g.merge(g2);
        
        return g;
    }

    @Override
    public DrtApplicationExpression getTemplate(Map<String,String> map) {
        DrtExpression newFunc = this.func.getTemplate(map);
        DrtExpression newArg = this.arg.getTemplate(map);
        return new DrtApplicationExpression(newFunc, newArg);
    }
}
